package com.linkgie.galaxyframework.crypto;

import com.linkgie.galaxyframework.utils.BinaryUtils;

/**
 * 校验和生成器；
 * 
 * @author huanghaiquan
 *
 * @param <T>
 */
public class ChecksumGenerator<T> {

	private MessageGenerator<T> messageGenerator;

	private final long SEED;

	private final String SEPERATOR;

	private final int CHECKSUM_LENGTH;

	private final HashService HASH_SERVICE;

	/**
	 * 创建校验和生成器；
	 * 
	 * @param seed            校验和种子；
	 * @param seperator       消息字段分隔符；
	 * @param checksumLength  校验和的字节长度；
	 * @param hashService     使用的哈希算法服务；
	 * @param versionReporter 版本报告函数；用于从输入数据中获得该数据的结构版本；
	 */
	public ChecksumGenerator(long seed, String seperator, int checksumLength, String hashAlgorithm, MessageGenerator<T> messageGenerator) {
		if (checksumLength <= 0) {
			throw new IllegalArgumentException("The specified checksum length is zero or negative!");
		}
		this.SEED = seed;
		this.SEPERATOR = seperator;
		this.CHECKSUM_LENGTH = checksumLength;
		this.HASH_SERVICE = Crypto.getHashService(hashAlgorithm);
		this.messageGenerator = messageGenerator;
	}

	/**
	 * 生成校验和；
	 * 
	 * @param input
	 * @return
	 */
	public byte[] generate(T input) {
		StringMessage message = new StringMessage();
		messageGenerator.generate(input, message);

		message.append(SEED);

		String stringMessage = message.getData();
		byte[] byteMessage = BinaryUtils.toBytes_UTF8(stringMessage);
		byte[] digest = HASH_SERVICE.hashInRaw(byteMessage);

		return BinaryUtils.copyRange(digest, 0, CHECKSUM_LENGTH);
	}

	/**
	 * 字符串格式的消息；
	 * 
	 * @author huanghaiquan
	 *
	 */
	private class StringMessage implements Message {

		private StringBuilder stringBuilder = new StringBuilder();

		private String getData() {
			return stringBuilder.toString();
		}

		@Override
		public Message append(String[] value) {
			if (value != null) {
				stringBuilder.append("[");
				stringBuilder.append(String.join(",", value));
				stringBuilder.append("]");
			}
			stringBuilder.append(SEPERATOR);
			return this;
		}
		
		@Override
		public Message append(CharSequence value) {
			if (value == null) {
				stringBuilder.append(SEPERATOR);
			} else {
				stringBuilder.append(value).append(SEPERATOR);
			}
			return this;
		}

		@Override
		public Message append(CharSequence value, int start, int end) {
			if (value == null) {
				stringBuilder.append(SEPERATOR);
			} else {
				stringBuilder.append(value, start, end).append(SEPERATOR);
			}
			return this;
		}

		@Override
		public Message append(char value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(char[] value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(char[] value, int offset, int len) {
			stringBuilder.append(value, offset, len).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Character value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(boolean value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Boolean value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(byte value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Byte value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(int value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Integer value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(short value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Short value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(long value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Long value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(float value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Float value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(double value) {
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}

		@Override
		public Message append(Double value) {
			if (value == null) {
				return append("");
			}
			stringBuilder.append(value).append(SEPERATOR);
			return this;
		}


	}

	/**
	 * 要计算校验和的消息；
	 * 
	 * @author huanghaiquan
	 *
	 */
	public static interface Message {
		
		Message append(String[] value);

		Message append(CharSequence value);

		Message append(CharSequence value, int start, int end);

		Message append(char value);

		Message append(char[] value);

		Message append(char[] value, int offset, int len);

		Message append(Character value);

		Message append(boolean value);

		Message append(Boolean value);

		Message append(byte value);

		Message append(Byte value);

		Message append(int value);

		Message append(Integer value);

		Message append(short value);

		Message append(Short value);

		Message append(long value);

		Message append(Long value);

		Message append(float value);

		Message append(Float value);

		Message append(double value);

		Message append(Double value);

	}

	/**
	 * 校验和消息生成器；
	 * 
	 * @author huanghaiquan
	 *
	 * @param <T>
	 */
	public static interface MessageGenerator<T> {

		/**
		 * 生成用于产生校验和的消息；
		 * <p>
		 * 实现者将要用于生成校验和的数据加入到消息中；
		 * 
		 * @param input
		 * @param message
		 */
		void generate(T input, Message message);

	}
}
